package com.qd.panda.service.space;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.math.Money;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.dto.ApiResult;
import com.cdqidi.dto.ApiResultWrapper;
import com.cdqidi.excel.util.ExportPage;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.JacksonUtil;
import com.cdqidi.util.SpringContextHolder;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qd.common.panda.config.PandaParkConfigDTO;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.info.ParkInfoDTO;
import com.qd.common.panda.domain.entity.space.*;
import com.qd.common.panda.domain.entity.stroke.PayParamDTO;
import com.qd.common.panda.domain.entity.stroke.UserStrokeOrderDTO;
import com.qd.common.sys.domain.sendmsg.FusionMsg;
import com.qd.common.sys.domain.sendmsg.MsgReceiver;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.panda.model.CarParkRevenue;
import com.qd.panda.model.ParkInfo;
import com.qd.panda.model.ParkSpace;
import com.qd.panda.model.ParkSpaceDate;
import com.qd.panda.service.info.ParkInfoService;
import com.qd.panda.service.park.CarParkLocationService;
import com.qd.panda.service.park.CarParkRevenueService;
import com.qd.panda.service.park.CarParkService;
import com.qd.panda.service.stroke.DisplayUtil;
import com.qd.panda.service.stroke.UserStrokeHolderService;
import com.qd.panda.service.stroke.order.UserStrokeOrderService;
import com.qd.system.service.msg.client.FusionMsgClientService;
import com.qd.system.util.FileConfirmUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static java.util.stream.Collectors.toList;

/**
 * 〈一句话功能简述〉<br>
 *
 * @author sjk
 * @create 2023-08-18 14:33
 * @since 1.0.0
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ParkSpaceHolderService {

    private final ParkSpaceService parkSpaceService;
    private final ParkSpaceTenantService parkSpaceTenantService;
    private final ParkSpaceDateService parkSpaceDateService;
    private final CarParkService carParkService;
    private final ParkInfoService parkInfoService;
    private final RedissonClient redissonClient;
    private final ParkSpaceRemindService parkSpaceRemindService;
    private final UserStrokeOrderService userStrokeOrderService;
    private final CarParkRevenueService carParkRevenueService;
    private final CarParkLocationService carParkLocationService;
    @Resource
    private FusionMsgClientService fusionMsgClientService;
    @Resource
    private PandaParkConfigDTO parkConfigDTO;

    @Transactional(rollbackFor = Exception.class)
    public PayParamDTO lease(ParkSpaceTenantDTO dto) {
        String userId = SecurityUtils.getLoginUser().getUserId();
        dto.setId(IdWorker.getIdStr());
        dto.setTenant(userId);
        parkSpaceService.checkDays(dto);
        ParkSpaceDTO parkSpace = Optional.ofNullable(parkSpaceService.getByIdDto(dto.getSid())).orElseThrow(() -> new ApiException("租赁ID不存在"));
        if (parkSpace.getEnd().equals(ConstantDto.SF1)) {
            throw new ApiException("当前租赁已经结束");
        }
        dto.setOrderTime(LocalDateTime.now());
        dto.setRefundAmount(new BigDecimal("0"));
        List<CarParkRevenue> carParkRevenueList = carParkRevenueService.getListByPid(parkSpace.getParkId());
        if (carParkRevenueList.isEmpty()) {
            throw new ApiException("请设置停车场收益");
        }
        CarParkRevenue carParkRevenue = getCarParkRevenue(dto, parkSpace, carParkRevenueList);
        //总费用
        BigDecimal totalCost = new Money(carParkRevenue.getDayRevenue()).multiply(dto.getDays()).getAmount();
        dto.setDayPrice(carParkRevenue.getDayRevenue());
        dto.setTotalCost(totalCost);
        //车主总收益
        Money deductionTotalRevenue = new Money(totalCost).multiply(parkSpace.getShareRatio()).divide(100);
        dto.setDeductionTotalRevenue(deductionTotalRevenue.getAmount());
        //扣费金额
        BigDecimal deductionAmount = new Money(totalCost).subtract(deductionTotalRevenue).getAmount();
        dto.setDeductionAmount(deductionAmount);
        dto.setTenantOrderState(DisplayUtil.PAY_STATUS_0);
        parkSpaceDateService.updateParkSpaceDate(dto);
        List<ParkSpaceDate> list = parkSpaceDateService.getListBySid(dto.getSid());
        if (!list.isEmpty()) {
            //获得没有人选择的日期
            //判断是否有两个日期是连续的
            boolean b;
            if (parkSpace.getLeaseType().equals(PandaConstant.LEASE_TYPE_DAY)) {
                b = DateUtil.isConsecutiveDates(list.stream().filter(d -> !StringUtils.hasLength(d.getTenant())).map(p -> p.getLeaseTime().toLocalDate()).toArray(LocalDate[]::new), 2);
            } else {
                b = DateUtil.isConsecutiveTimes(list.stream().filter(d -> !StringUtils.hasLength(d.getTenant())).map(p -> p.getLeaseTime().toLocalTime()).toArray(LocalTime[]::new), 2);
            }
            if (!b) {
                //没有连续两个可选日期,那么是否可租改成否
                parkSpace.setRentable(ConstantDto.SF0);
                parkSpaceService.update(parkSpace);
            }
        }
        parkSpaceTenantService.save(dto);
        log.info("租赁金额,totalCost: {}", totalCost);
        //测试，支付金额设置成1分钱
        if (!SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_OFFICIAL)) {
            totalCost = new BigDecimal("0.01");
        }
        PayParamDTO orderPay = SpringContextHolder.getBean(UserStrokeHolderService.class).createOrderPay(parkSpace.getParkId(), dto.getId(), totalCost, DisplayUtil.ORDER_TYPE_6);
        parkSpace.freeData();
        orderPay.setThirdPartyOrder(dto.getId());
        return orderPay;
    }

    private CarParkRevenue getCarParkRevenue(ParkSpaceTenantDTO dto, ParkSpaceDTO parkSpace, List<CarParkRevenue> carParkRevenueList) {
        CarParkRevenue carParkRevenue = null;
        if (StringUtils.hasLength(parkSpace.getLeaseType())) {
            Optional<CarParkRevenue> optional;
            if (parkSpace.getLeaseType().equals(PandaConstant.LEASE_TYPE_DAY)) {
                optional = carParkRevenueList.stream().filter(p -> p.getLeaseType().equals(parkSpace.getLeaseType())).filter(p -> p.getDays() >= dto.getDays()).max(Comparator.comparingInt(o -> o.getDayRevenue().multiply(BigDecimal.valueOf(dto.getDays())).intValue()));
            } else {
                optional = carParkRevenueList.stream().filter(p -> p.getLeaseType().equals(parkSpace.getLeaseType())).max(Comparator.comparingInt(o -> o.getDayRevenue().multiply(BigDecimal.valueOf(dto.getDays())).intValue()));
            }
            if (optional.isEmpty()) {
                throw new ApiException("没有符合要求的收益");
            }
            carParkRevenue = optional.get();
        }
        if (null == carParkRevenue) {
            throw new ApiException("没有符合要求的收益");
        }
        return carParkRevenue;
    }

    public void updateErr(ParkSpaceTenantDTO parkSpaceTenant) {
        ParkSpaceDTO parkSpace = parkSpaceService.getByIdDto(parkSpaceTenant.getSid());
        if (null != parkSpace && parkSpace.getRentable().equals(ConstantDto.SF0)) {
            //因为有人支付失败了，所以如果是否可租为否的话，那么就要修改为是
            parkSpace.setRentable(ConstantDto.SF1);
            parkSpaceService.update(parkSpace);
            parkSpace.freeData();
        }
        LocalDate start = DateUtil.strToLocalDate(parkSpaceTenant.getStartDate());
        LocalDate end = DateUtil.strToLocalDate(parkSpaceTenant.getEndDate());
        parkSpaceTenant.setTenantOrderState(DisplayUtil.PAY_STATUS_2);
        List<ParkSpaceDate> list = parkSpaceDateService.getListBySid(parkSpaceTenant.getSid())
                .stream().filter(parkSpaceDate -> StringUtils.hasLength(parkSpaceDate.getTenant())).filter(parkSpaceDate -> parkSpaceDate.getTenant().equals(parkSpaceTenant.getTenant())).collect(toList());
        if (list.isEmpty()) {
            return;
        }
        List<ParkSpaceDate> updateList = new ArrayList<>(list.size());
        List<LocalDate> dateList = DateUtil.getDatesBetween(start, end);
        for (ParkSpaceDate parkSpaceDate : list) {
            if (CollUtil.contains(dateList, parkSpaceDate.getLeaseTime())) {
                parkSpaceDate.setTenant(null);
                parkSpaceDate.setTenantNumber(null);
                updateList.add(parkSpaceDate);
            }
        }
        if (!updateList.isEmpty()) {
            parkSpaceDateService.updateBatchIds(updateList);
            updateList.clear();
        }
        list.clear();
        dateList.clear();
        parkSpaceTenantService.update(parkSpaceTenant);
    }


    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void updateSuccess(ParkSpaceTenantDTO parkSpaceTenant) {
        parkSpaceTenant.setTenantOrderState(DisplayUtil.PAY_STATUS_1);
        parkSpaceTenantService.update(parkSpaceTenant);
        ParkSpaceDTO parkSpaceDTO = parkSpaceService.getByIdDto(parkSpaceTenant.getSid());
        if (null == parkSpaceDTO) {
            return;
        }
        List<ParkSpaceTenantDTO> list = parkSpaceTenantService.getListBySidDto(parkSpaceTenant.getSid());
        Money totalRevenue = new Money();
        Money deductionTotalRevenue = new Money();
        Money deductionAmount = new Money();
        for (ParkSpaceTenantDTO parkSpaceTenantDTO : list) {
            totalRevenue = totalRevenue.add(new Money(parkSpaceTenantDTO.getTotalCost()));
            deductionTotalRevenue = deductionTotalRevenue.add(new Money(parkSpaceTenantDTO.getDeductionTotalRevenue()));
            deductionAmount = deductionAmount.add(new Money(parkSpaceTenantDTO.getDeductionAmount()));
        }
        //总收益
        parkSpaceDTO.setTotalRevenue(totalRevenue.getAmount());
        //扣费后总收益
        parkSpaceDTO.setDeductionTotalRevenue(deductionTotalRevenue.getAmount());
        //扣费金额
        parkSpaceDTO.setDeductionAmount(deductionAmount.getAmount());
        parkSpaceService.update(parkSpaceDTO);
        //发送提醒承租人的模板消息,现在改成不发，要到期前的中午12点发
//        sendOwnTemplateMessage(parkSpaceDTO, parkSpaceTenant);
        //发送提醒租赁人的模板消息
        LocalDateTime now = LocalDateTime.now();
        if (parkSpaceTenant.getStartDate().equals(DateUtil.localDateToStr(now.toLocalDate()))) {
            int hour = now.getHour();
            if (hour >= 12) {
                sendCarOwnTemplateMessage(parkSpaceDTO, parkSpaceTenant);
            }
        }
        //同步到用户订单(支付成功的才需要同步到订单表，方便三虎哥查询)
        saveUserStrokeOrder(parkSpaceDTO, parkSpaceTenant);
        parkSpaceDTO.freeData();
        parkSpaceTenant.freeData();
    }


    public void saveUserStrokeOrder(ParkSpaceDTO parkSpaceDTO, ParkSpaceTenantDTO parkSpaceTenant) {
        UserStrokeOrderDTO userStrokeOrderDTO = userStrokeOrderService.getByIdDto(parkSpaceTenant.getId());
        if (null == userStrokeOrderDTO) {
            userStrokeOrderDTO = new UserStrokeOrderDTO();
            userStrokeOrderDTO.setOrderId(parkSpaceTenant.getId());
            userStrokeOrderDTO.setBid(parkSpaceTenant.getId());
            userStrokeOrderDTO.setStartTime(parkSpaceTenant.getOrderTime());
            userStrokeOrderDTO.setOrderAmount(parkSpaceTenant.getTotalCost());
            userStrokeOrderDTO.setPayAmount(parkSpaceTenant.getTotalCost());
            userStrokeOrderDTO.setOnLinePayAmount(parkSpaceTenant.getTotalCost());
            userStrokeOrderDTO.setDiscountAmount(new BigDecimal("0"));
            userStrokeOrderDTO.setPayStatus(parkSpaceTenant.getTenantOrderState());
            userStrokeOrderDTO.setOrderType(DisplayUtil.ORDER_TYPE_6);
            userStrokeOrderDTO.setParkId(parkSpaceDTO.getParkId());
            userStrokeOrderDTO.setParkIdDisplay(carParkService.idToDisplay(parkSpaceDTO.getParkId()));
            userStrokeOrderDTO.setPlateNumber(parkSpaceTenant.getTenantNumber());
            userStrokeOrderDTO.setUserId(parkSpaceTenant.getTenant());
            userStrokeOrderDTO.setTripType(DisplayUtil.TRIP_TYPE_3);
            userStrokeOrderDTO.setEtcPay(ConstantDto.SF0);
            userStrokeOrderService.save(userStrokeOrderDTO);
        }
        userStrokeOrderDTO.freeData();
    }


    private void sendCarOwnTemplateMessage(ParkSpaceDTO parkSpaceDTO, ParkSpaceTenantDTO parkSpaceTenant) {
        ParkSpaceRemindDTO spaceRemindDTO = parkSpaceRemindService.getByIdDto(parkSpaceTenant.getSid());
        if (null == spaceRemindDTO) {
            spaceRemindDTO = new ParkSpaceRemindDTO();
            spaceRemindDTO.setSid(parkSpaceTenant.getSid());
            spaceRemindDTO.setId(parkSpaceTenant.getId());
            spaceRemindDTO.setAddTime(LocalDateTime.now());
            parkSpaceRemindService.save(spaceRemindDTO);

            String sysSpaceNumber = Optional.ofNullable(parkInfoService.getByIdDto(parkSpaceDTO.getAid())).map(ParkInfoDTO::getSysSpaceNumber).orElse("");
            log.info("sid: {},id: {},停车场名称: {},sysSpaceNumber: {},startDate: {},endDate: {}", parkSpaceTenant.getSid(), parkSpaceTenant.getId(), parkSpaceDTO.getParkIdDisplay(), sysSpaceNumber, parkSpaceTenant.getStartDate(), parkSpaceTenant.getEndDate());
            MsgReceiver receiver = new MsgReceiver();
            //人
            receiver.setId(parkSpaceDTO.getPublisher());
            //接收者类型
            receiver.setClassify(MsgReceiver.CLASSIFY_USER);
//        recv.getUrlParams().addProp("id", userId);
//        recv.getUrlParams().addProp("schoolId", "sch");
//        recv.getUrlParams().addProp("classId", "cls");
//        recv.getUrlParams().addProp("type", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1);
//        recv.getUrlParams().addProp("count", "undefined");
//        recv.getUrlParams().addProp("template", "1");
            receiver.getProperties().addProp("thing2", parkSpaceDTO.getParkIdDisplay());
            receiver.getProperties().addProp("character_string6", sysSpaceNumber);
            receiver.getProperties().addProp("date3", parkSpaceTenant.getStartDate());
            receiver.getProperties().addProp("date4", parkSpaceTenant.getEndDate());
            receiver.getProperties().addProp("thing5", "您的泊位已成功租赁");
            fusionMsgClientService.sendMsg(parkConfigDTO.getDefaultMiniProgramAppId(), null, PandaConstant.T_1001, parkSpaceTenant.getSid(), parkSpaceTenant.getId(), "泊位成功租赁", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1, receiver, FusionMsg.SEND_FLAG_WEIXIN);
        }
        spaceRemindDTO.freeData();
    }


    /**
     * 修改结束状态
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateEnd() {
        QueryWrapper<ParkSpace> query = new QueryWrapper<>();
        query.eq("end", ConstantDto.SF0);
        query.lt("end_date", LocalDate.now());
        final ExportPage<ParkSpace, ParkSpace> exportPage = new ExportPage<>();
        try {
            final List<ParkSpace> list = exportPage.getAllForPage(page -> parkSpaceService.page(page, query).getRecords());
            if (!list.isEmpty()) {
                list.forEach(parkSpace -> {
                    parkSpace.setEnd(ConstantDto.SF1);
                    parkSpace.setRentable(ConstantDto.SF0);
                    parkSpace.setState(StateUtilService.STATE_2);

                });
                parkSpaceService.updateBatchIds(list);
                list.clear();
            }
        } finally {
            query.clear();
            exportPage.freeData();
        }
    }

    @Transactional(readOnly = true)
    public Object parkCode() {
        List<CarOwnerParkSpaceDTO> list = parkSpaceService.getBaseMapper().rentalInformation(SecurityUtils.getLoginUser().getUserId(), LocalDate.now());
        if (null == list || list.isEmpty()) {
            throw new ApiException(ApiResultWrapper.NULL_ERROR, "数据为空");
        } else {
            LocalDate now = LocalDate.now();
            ArrayNode arrayNode = JacksonUtil.getObjectMapper().createArrayNode();
            for (CarOwnerParkSpaceDTO carOwnerParkSpaceDTO : list) {
                ObjectNode objectNode = JacksonUtil.getObjectMapper().createObjectNode();
                objectNode.put("sid", carOwnerParkSpaceDTO.getSid());
                objectNode.put("id", carOwnerParkSpaceDTO.getId());
                objectNode.put("parkId", carOwnerParkSpaceDTO.getParkId());
                objectNode.put("parkIdDisplay", carOwnerParkSpaceDTO.getParkIdDisplay());
                objectNode.put("spaceNumber", carOwnerParkSpaceDTO.getSpaceNumber());
                objectNode.put("sysSpaceNumber", carOwnerParkSpaceDTO.getSysSpaceNumber());
                objectNode.put("startDate", carOwnerParkSpaceDTO.getStartDate());
                objectNode.put("endDate", carOwnerParkSpaceDTO.getEndDate());
                objectNode.put("planPic", FileConfirmUtil.getImageFile(carOwnerParkSpaceDTO.getPlanPic()));
                objectNode.put("note", carOwnerParkSpaceDTO.getNote());
                objectNode.put("address", carOwnerParkSpaceDTO.getAddress());
                objectNode.put("tenantNumber", carOwnerParkSpaceDTO.getTenantNumber());
                //0表示未生效，1表示生效中
                int state = 0;
                LocalDate startDate = DateUtil.strToLocalDate(carOwnerParkSpaceDTO.getStartDate());
                if (now.equals(startDate)) {
                    state = 1;
                }
                if (now.isAfter(startDate)) {
                    state = 1;
                }
                objectNode.put("state", state);
                Optional.ofNullable(carParkLocationService.getById(carOwnerParkSpaceDTO.getParkId())).ifPresent(carParkLocation -> {
                    objectNode.put("lat", carParkLocation.getLat());
                    objectNode.put("lng", carParkLocation.getLng());
                });
                arrayNode.add(objectNode);
            }
            return arrayNode;
        }
    }

    @Transactional(readOnly = true)
    public ObjectNode ownParkAmountDetailBySid(String sid) {
        ObjectNode objectNode = JacksonUtil.getObjectMapper().createObjectNode();
        ParkSpace parkSpace = Optional.ofNullable(parkSpaceService.getById(sid)).orElseThrow(() -> new ApiException("租赁ID值不存在"));
        objectNode.put("sid", parkSpace.getSid());
        objectNode.put("leasableStartDate", parkSpace.getStartDate().toString());
        objectNode.put("leasableEndDate", parkSpace.getEndDate().toString());
        objectNode.put("deductionTotalRevenue", parkSpace.getDeductionTotalRevenue().stripTrailingZeros().toPlainString());
        objectNode.put("parkId", parkSpace.getParkId());
        objectNode.put("parkIdDisplay", carParkService.idToDisplay(parkSpace.getParkId()));
        ParkInfo parkInfo = Optional.ofNullable(parkInfoService.getById(parkSpace.getAid())).orElseThrow(() -> new ApiException("aid不存在"));
        objectNode.put("spaceNumber", parkInfo.getSpaceNumber());
        objectNode.put("sysSpaceNumber", parkInfo.getSysSpaceNumber());
        List<ParkSpaceTenantDTO> list = parkSpaceTenantService.getBaseMapper().ownParkSpaceTenantPage(sid);
        if (!list.isEmpty()) {
            LocalDate now = LocalDate.now();
            ArrayNode arrayNode = JacksonUtil.getObjectMapper().createArrayNode();
            for (ParkSpaceTenantDTO parkSpaceTenantDTO : list) {
                ObjectNode node = JacksonUtil.getObjectMapper().createObjectNode();
                node.put("id", parkSpaceTenantDTO.getId());
                node.put("sid", parkSpaceTenantDTO.getSid());
                node.put("tenantNumber", parkSpaceTenantDTO.getTenantNumber());
                node.put("startDate", parkSpaceTenantDTO.getStartDate());
                node.put("endDate", parkSpaceTenantDTO.getEndDate());
                node.put("orderTime", parkSpaceTenantDTO.getOrderTime().toString());
                //0表示进行中，1表示已结束,2未开始
                int state = 0;
                LocalDate endDate = DateUtil.strToLocalDate(parkSpaceTenantDTO.getEndDate());
                if (now.isAfter(endDate)) {
                    state = 1;
                }
                LocalDate startDate = DateUtil.strToLocalDate(parkSpaceTenantDTO.getStartDate());
                if (now.isBefore(startDate)) {
                    state = 2;
                }
                node.put("state", state);
                node.put("deductionTotalRevenue", parkSpaceTenantDTO.getDeductionTotalRevenue().stripTrailingZeros().toPlainString());
                arrayNode.add(node);
            }
            objectNode.set("orderList", arrayNode);
        }
        return objectNode;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updatePayState(String id) {
        ParkSpaceTenantDTO parkSpaceTenant = Optional.ofNullable(parkSpaceTenantService.getByIdDto(id)).orElseThrow(() -> new ApiException("ID值不存在"));
        RLock lock = redissonClient.getLock(parkSpaceTenant.getSid());
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    updateErr(parkSpaceTenant);
                    parkSpaceTenant.freeData();
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(UserStrokeHolderService.REDIS_LOCKED_ERR, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }


    @Transactional(readOnly = true)
    public void sendExpireMsg(LocalDate now) {
        List<ExpireSpaceTenantDTO> list = parkSpaceTenantService.getBaseMapper().expireList(now);
        List<OwnerMyIncomeSpaceDTO> mList = parkSpaceService.getBaseMapper().expireList(now);
        List<AdmissionDTO> admissionList = parkSpaceTenantService.getBaseMapper().admission(now);
        LocalDateTime localDateTime = LocalDateTime.now().withHour(12).withMinute(0).withSecond(0);
        long time = localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long nowTime = System.currentTimeMillis();
        long l = time - nowTime;
        if (l > 0) {
            try {
                Thread.sleep(l);
            } catch (InterruptedException e) {
                log.error("Interrupted!", e);
                Thread.currentThread().interrupt();
            }
        }
        if (null != list && !list.isEmpty()) {
            for (ExpireSpaceTenantDTO expireSpaceTenantDTO : list) {
                sendOwn(expireSpaceTenantDTO);
            }
        }
        if (null != mList && !mList.isEmpty()) {
            for (OwnerMyIncomeSpaceDTO ownerMyIncomeSpaceDTO : mList) {
                sendCarOwn(ownerMyIncomeSpaceDTO);
            }
        }
        if (null != admissionList && !admissionList.isEmpty()) {
            for (AdmissionDTO admissionDTO : admissionList) {
                sendOwnTemplateMessage(admissionDTO);
            }
        }
    }

    private void sendOwn(ExpireSpaceTenantDTO expireSpaceTenantDTO) {
        MsgReceiver receiver = new MsgReceiver();
        //人
        receiver.setId(expireSpaceTenantDTO.getTenant());
        //接收者类型
        receiver.setClassify(MsgReceiver.CLASSIFY_USER);
//        recv.getUrlParams().addProp("id", userId);
//        recv.getUrlParams().addProp("schoolId", "sch");
//        recv.getUrlParams().addProp("classId", "cls");
//        recv.getUrlParams().addProp("type", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1);
//        recv.getUrlParams().addProp("count", "undefined");
//        recv.getUrlParams().addProp("template", "1");
        receiver.getProperties().addProp("car_number5", expireSpaceTenantDTO.getTenantNumber());
        receiver.getProperties().addProp("thing2", expireSpaceTenantDTO.getName());
        receiver.getProperties().addProp("thing4", expireSpaceTenantDTO.getSysSpaceNumber());
        receiver.getProperties().addProp("thing6", "12小时");
        receiver.getProperties().addProp("thing12", "请在车位到期前出场，过时将产生额外费用！");
        fusionMsgClientService.sendMsg(parkConfigDTO.getDefaultMiniProgramAppId(), null, PandaConstant.T_1002, expireSpaceTenantDTO.getSid(), expireSpaceTenantDTO.getId(), "车主端车位到期提醒", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1, receiver, FusionMsg.SEND_FLAG_WEIXIN | FusionMsg.SEND_FLAG_SMS);
    }

    private void sendCarOwn(OwnerMyIncomeSpaceDTO ownerMyIncomeSpaceDTO) {
        MsgReceiver receiver = new MsgReceiver();
        //人
        receiver.setId(ownerMyIncomeSpaceDTO.getPublisher());
        //接收者类型
        receiver.setClassify(MsgReceiver.CLASSIFY_USER);
//        recv.getUrlParams().addProp("id", userId);
//        recv.getUrlParams().addProp("schoolId", "sch");
//        recv.getUrlParams().addProp("classId", "cls");
//        recv.getUrlParams().addProp("type", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1);
//        recv.getUrlParams().addProp("count", "undefined");
//        recv.getUrlParams().addProp("template", "1");
        receiver.getProperties().addProp("thing2", ownerMyIncomeSpaceDTO.getParkIdDisplay());
        receiver.getProperties().addProp("character_string6", ownerMyIncomeSpaceDTO.getSysSpaceNumber());
        receiver.getProperties().addProp("date3", ownerMyIncomeSpaceDTO.getStartDate());
        receiver.getProperties().addProp("date4", ownerMyIncomeSpaceDTO.getEndDate());
        receiver.getProperties().addProp("thing5", "您的泊位已租赁至最后一天，请不要提前返回哟！");
        fusionMsgClientService.sendMsg(parkConfigDTO.getDefaultMiniProgramAppId(), null, PandaConstant.T_1001, ownerMyIncomeSpaceDTO.getSid(), "", "业主端车位到期提醒", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1, receiver, FusionMsg.SEND_FLAG_WEIXIN | FusionMsg.SEND_FLAG_SMS);
    }

    private void sendOwnTemplateMessage(ParkSpaceDTO parkSpaceDTO, ParkSpaceTenantDTO parkSpaceTenant) {
        String sysSpaceNumber = Optional.ofNullable(parkInfoService.getByIdDto(parkSpaceDTO.getAid())).map(ParkInfoDTO::getSysSpaceNumber).orElse("");
        AdmissionDTO admissionDTO = new AdmissionDTO();
        admissionDTO.setSysSpaceNumber(sysSpaceNumber);
        admissionDTO.setTenant(parkSpaceTenant.getTenant());
        admissionDTO.setTenantNumber(parkSpaceTenant.getTenantNumber());
        admissionDTO.setParkIdDisplay(parkSpaceDTO.getParkIdDisplay());
        admissionDTO.setStartDate(parkSpaceTenant.getStartDate());
        admissionDTO.setSid(parkSpaceTenant.getSid());
        admissionDTO.setId(parkSpaceTenant.getId());
        sendOwnTemplateMessage(admissionDTO);
    }

    private void sendOwnTemplateMessage(AdmissionDTO admissionDTO) {
        log.info("====sid: {},id: {},tenant: {},tenantNumber: {},停车场名称: {},sysSpaceNumber: {},startDate: {}", admissionDTO.getSid(), admissionDTO.getId(), admissionDTO.getTenant(), admissionDTO.getTenantNumber(), admissionDTO.getParkIdDisplay(), admissionDTO.getSysSpaceNumber(), admissionDTO.getStartDate());
        MsgReceiver receiver = new MsgReceiver();
        //人
        receiver.setId(admissionDTO.getTenant());
        //接收者类型
        receiver.setClassify(MsgReceiver.CLASSIFY_USER);
//        recv.getUrlParams().addProp("id", userId);
//        recv.getUrlParams().addProp("schoolId", "sch");
//        recv.getUrlParams().addProp("classId", "cls");
//        recv.getUrlParams().addProp("type", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1);
//        recv.getUrlParams().addProp("count", "undefined");
//        recv.getUrlParams().addProp("template", "1");
        receiver.getProperties().addProp("car_number2", admissionDTO.getTenantNumber());
        receiver.getProperties().addProp("thing1", admissionDTO.getParkIdDisplay());
        receiver.getProperties().addProp("character_string5", admissionDTO.getSysSpaceNumber());
        receiver.getProperties().addProp("time3", admissionDTO.getStartDate());
        receiver.getProperties().addProp("thing6", "您预约的停车时间已到，请尽快前往停车地点");
        log.info("===========================================================================");
        fusionMsgClientService.sendMsg(parkConfigDTO.getDefaultMiniProgramAppId(), null, PandaConstant.T_1003, admissionDTO.getSid(), admissionDTO.getId(), "预约入场", PandaConstant.MSG_EVENT_MSG_CLASSIFY_1, receiver, FusionMsg.SEND_FLAG_WEIXIN);
    }

    @Transactional(rollbackFor = Exception.class)
    public void refund(ParkSpaceTenantDTO dto) {
        String id = dto.getId();
        LocalDateTime now = LocalDateTime.now();
        ParkSpaceTenantDTO parkSpaceTenantDTO = Optional.ofNullable(parkSpaceTenantService.getByIdDto(id)).orElseThrow(() -> new ApiException("ID值不存在"));
        if (!parkSpaceTenantDTO.getTenant().equals(SecurityUtils.getLoginUser().getUserId())) {
            log.error("没有操作权限,登录人ID: {},订单支付人ID： {}", SecurityUtils.getLoginUser().getUserId(), parkSpaceTenantDTO.getTenant());
            throw new ApiException("没有操作权限");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_0)) {
            throw new ApiException("未支付不能退款");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_2)) {
            throw new ApiException("付款失败不能退款");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_7)) {
            throw new ApiException("退款中,请勿重复申请");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_8)) {
            throw new ApiException("已经退款成功,请勿重复申请");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_9)) {
            throw new ApiException("退款失败,联系工作人员处理");
        }
        if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_1)) {
            Date date = DateUtil.localDateToDate(now.toLocalDate());
            Date startDate = DateUtil.strToDate(parkSpaceTenantDTO.getStartDate());
            long between = cn.hutool.core.date.DateUtil.between(date, startDate, DateUnit.DAY);
            if (between < 1) {
                log.info("当前日期: {},开始日期: {},between: {}", date, startDate, between);
                throw new ApiException("不能取消");
            }
            BigDecimal totalCost = parkSpaceTenantDTO.getTotalCost();
            if (!SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_OFFICIAL)) {
                totalCost = new BigDecimal("0.01");
            }
            //请求天府银行完成退款操作
            UserStrokeHolderService userStrokeHolderService = SpringContextHolder.getBean(UserStrokeHolderService.class);
            userStrokeHolderService.createRefundOrder(parkSpaceTenantDTO.getId(), parkSpaceTenantDTO.getId(), parkSpaceTenantDTO.getTenant(), totalCost, now);
            parkSpaceTenantDTO = parkSpaceTenantService.getByIdDto(id);
            if (parkSpaceTenantDTO.getTenantOrderState().equals(DisplayUtil.PAY_STATUS_1)) {
                //处理付款成功的
                parkSpaceTenantDTO.setTenantOrderState(DisplayUtil.PAY_STATUS_7);
                parkSpaceTenantDTO.setRefundAmount(parkSpaceTenantDTO.getTotalCost());
                parkSpaceTenantDTO.setRefundTime(now);
                parkSpaceTenantDTO.setRefundNote("用户主动发起退款操作");
                parkSpaceTenantService.update(parkSpaceTenantDTO);

                updateParkSpace(parkSpaceTenantDTO);
                updateParkSpaceDate(parkSpaceTenantDTO);

                parkSpaceTenantDTO.freeData();
            }
        } else {
            log.error("其他状态，处理不了: {}", parkSpaceTenantDTO.getTenantOrderState());
        }
    }

    private void updateParkSpace(ParkSpaceTenantDTO parkSpaceTenantDTO) {
        ParkSpaceDTO parkSpaceDTO = parkSpaceService.getByIdDto(parkSpaceTenantDTO.getSid());
        if (null != parkSpaceDTO) {
            if (parkSpaceDTO.getRentable().equals(ConstantDto.SF0)) {
                //修改是否可租为是
                parkSpaceDTO.setRentable(ConstantDto.SF1);
                parkSpaceService.update(parkSpaceDTO);
            }
            parkSpaceDTO.freeData();
        }
    }

    private void updateParkSpaceDate(ParkSpaceTenantDTO parkSpaceTenantDTO) {
        List<ParkSpaceDate> list = parkSpaceDateService.getListBySid(parkSpaceTenantDTO.getSid());
        if (!list.isEmpty()) {
            List<LocalDate> dateList = DateUtil.getDatesBetween(DateUtil.strToLocalDate(parkSpaceTenantDTO.getStartDate()), DateUtil.strToLocalDate(parkSpaceTenantDTO.getEndDate()));
            for (LocalDate localDate : dateList) {
                log.info("localDate: {}", localDate);
            }
            List<ParkSpaceDate> parkSpaceDateList = list.stream().filter(p -> {
                if (StringUtils.hasLength(p.getTenant())) {
                    return p.getTenant().equals(parkSpaceTenantDTO.getTenant());
                }
                return false;
            }).filter(parkSpaceDate -> {
                if (parkSpaceDate.getLeaseTime() != null) {
                    return dateList.contains(parkSpaceDate.getLeaseTime());
                }
                return false;
            }).peek(parkSpaceDate -> {
                parkSpaceDate.setTenant(null);
                parkSpaceDate.setTenantNumber(null);
            }).collect(toList());
            if (!parkSpaceDateList.isEmpty()) {
                parkSpaceDateService.updateBatchIds(parkSpaceDateList);
                parkSpaceDateList.clear();
            }
            dateList.clear();
            parkSpaceTenantDTO.freeData();
        }
    }

}